دليل شامل لفهم وتنفيذ مشاركة الموارد عبر المصادر (CORS) لتأمين التواصل باستخدام جافاسكريبت بين النطاقات المختلفة.
تنفيذ أمان الوصول عبر المصادر: أفضل ممارسات التواصل باستخدام جافاسكريبت
في عالم الويب المترابط اليوم، تحتاج تطبيقات جافاسكريبت بشكل متكرر إلى التفاعل مع موارد من مصادر مختلفة (نطاقات، بروتوكولات، أو منافذ). يخضع هذا التفاعل لـ "سياسة نفس المصدر" في المتصفح، وهي آلية أمان حاسمة مصممة لمنع النصوص البرمجية الخبيثة من الوصول إلى البيانات الحساسة عبر حدود النطاقات. ومع ذلك، غالبًا ما يكون التواصل المشروع عبر المصادر ضروريًا. هنا يأتي دور مشاركة الموارد عبر المصادر (CORS). يقدم هذا المقال نظرة شاملة على CORS، وكيفية تنفيذه، وأفضل الممارسات لتأمين التواصل عبر المصادر في جافاسكريبت.
فهم سياسة نفس المصدر
سياسة نفس المصدر (SOP) هي مفهوم أمني أساسي في متصفحات الويب. إنها تقيد النصوص البرمجية التي تعمل على مصدر معين من الوصول إلى موارد من مصدر مختلف. يُعرَّف المصدر بمزيج من البروتوكول (مثل HTTP أو HTTPS)، واسم النطاق (مثل example.com)، ورقم المنفذ (مثل 80 أو 443). يكون لعنواني URL نفس المصدر فقط إذا تطابقت المكونات الثلاثة تمامًا.
على سبيل المثال:
http://www.example.comوhttp://www.example.com/path: نفس المصدرhttp://www.example.comوhttps://www.example.com: مصدر مختلف (بروتوكول مختلف)http://www.example.comوhttp://subdomain.example.com: مصدر مختلف (نطاق مختلف)http://www.example.com:80وhttp://www.example.com:8080: مصدر مختلف (منفذ مختلف)
تعتبر سياسة نفس المصدر دفاعًا حاسمًا ضد هجمات البرمجة عبر المواقع (XSS)، حيث يمكن للنصوص البرمجية الخبيثة المحقونة في موقع ويب سرقة بيانات المستخدم أو تنفيذ إجراءات غير مصرح بها نيابة عن المستخدم.
ما هي مشاركة الموارد عبر المصادر (CORS)؟
CORS هي آلية تستخدم ترويسات HTTP للسماح للخوادم بتحديد المصادر (النطاقات، المخططات، أو المنافذ) المسموح لها بالوصول إلى مواردها. إنها تخفف بشكل أساسي من قيود سياسة نفس المصدر لطلبات معينة عبر المصادر، مما يتيح التواصل المشروع مع الحماية من الهجمات الخبيثة.
يعمل CORS عن طريق إضافة ترويسات HTTP جديدة تحدد المصادر المسموح بها والأساليب (مثل GET, POST, PUT, DELETE) المسموح بها للطلبات عبر المصادر. عندما يقوم المتصفح بطلب عبر المصادر، فإنه يرسل ترويسة Origin مع الطلب. يستجيب الخادم بترويسة Access-Control-Allow-Origin التي تحدد المصدر (المصادر) المسموح به. إذا تطابق مصدر الطلب مع القيمة في ترويسة Access-Control-Allow-Origin (أو إذا كانت القيمة *)، يسمح المتصفح لكود جافاسكريبت بالوصول إلى الاستجابة.
كيف يعمل CORS: شرح مفصل
تتضمن عملية CORS عادةً نوعين من الطلبات:
- الطلبات البسيطة: هي الطلبات التي تفي بمعايير محددة. إذا استوفى الطلب هذه الشروط، يرسل المتصفح الطلب مباشرة.
- الطلبات التمهيدية: هي طلبات أكثر تعقيدًا تتطلب من المتصفح أولاً إرسال طلب OPTIONS "تمهيدي" إلى الخادم لتحديد ما إذا كان الطلب الفعلي آمنًا للإرسال.
1. الطلبات البسيطة
يعتبر الطلب "بسيطًا" إذا استوفى جميع الشروط التالية:
- الأسلوب هو
GET،HEAD، أوPOST. - إذا كان الأسلوب
POST، فإن ترويسةContent-Typeتكون واحدة مما يلي: application/x-www-form-urlencodedmultipart/form-datatext/plain- لم يتم تعيين أي ترويسات مخصصة.
مثال على طلب بسيط:
GET /resource HTTP/1.1
Origin: http://www.example.com
مثال على استجابة الخادم التي تسمح بالمصدر:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Content-Type: application/json
{
"data": "Some data"
}
إذا كانت ترويسة Access-Control-Allow-Origin موجودة وقيمتها تطابق مصدر الطلب أو تم تعيينها على *، فإن المتصفح يسمح للنص البرمجي بالوصول إلى بيانات الاستجابة. وإلا، يمنع المتصفح الوصول إلى الاستجابة، وتُعرض رسالة خطأ في وحدة التحكم.
2. الطلبات التمهيدية
يعتبر الطلب "تمهيديًا" إذا لم يستوفِ معايير الطلب البسيط. يحدث هذا عادةً عندما يستخدم الطلب أسلوب HTTP مختلفًا (مثل PUT، DELETE)، أو يعين ترويسات مخصصة، أو يستخدم Content-Type غير القيم المسموح بها.
قبل إرسال الطلب الفعلي، يرسل المتصفح أولاً طلب OPTIONS إلى الخادم. يتضمن هذا الطلب "التمهيدي" الترويسات التالية:
Origin: مصدر الصفحة الطالبة.Access-Control-Request-Method: أسلوب HTTP الذي سيتم استخدامه في الطلب الفعلي (مثلPUT،DELETE).Access-Control-Request-Headers: قائمة مفصولة بفواصل للترويسات المخصصة التي سيتم إرسالها في الطلب الفعلي.
مثال على طلب تمهيدي:
OPTIONS /resource HTTP/1.1
Origin: http://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header, Content-Type
يجب على الخادم الاستجابة لطلب OPTIONS بالترويسات التالية:
Access-Control-Allow-Origin: المصدر المسموح له بتقديم الطلب (أو*للسماح لأي مصدر).Access-Control-Allow-Methods: قائمة مفصولة بفواصل لأساليب HTTP المسموح بها للطلبات عبر المصادر (مثلGET،POST،PUT،DELETE).Access-Control-Allow-Headers: قائمة مفصولة بفواصل للترويسات المخصصة المسموح بإرسالها في الطلب.Access-Control-Max-Age: عدد الثواني التي يمكن للمتصفح تخزين الاستجابة التمهيدية فيها مؤقتًا.
مثال على استجابة الخادم لطلب تمهيدي:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 86400
إذا أشارت استجابة الخادم للطلب التمهيدي إلى أن الطلب الفعلي مسموح به، فسيقوم المتصفح بإرسال الطلب الفعلي. وإلا، سيمنع المتصفح الطلب ويعرض رسالة خطأ.
تنفيذ CORS من جانب الخادم
يتم تنفيذ CORS بشكل أساسي من جانب الخادم عن طريق تعيين ترويسات HTTP المناسبة في الاستجابة. تختلف تفاصيل التنفيذ المحددة اعتمادًا على التقنية المستخدمة من جانب الخادم.
مثال باستخدام Node.js مع Express:
const express = require('express');
const cors = require('cors');
const app = express();
// تفعيل CORS لجميع المصادر
app.use(cors());
// بدلاً من ذلك، قم بتكوين CORS لمصادر محددة
// const corsOptions = {
// origin: 'http://www.example.com'
// };
// app.use(cors(corsOptions));
app.get('/resource', (req, res) => {
res.json({ message: 'This is a CORS-enabled resource' });
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
تبسط البرمجية الوسيطة cors عملية تعيين ترويسات CORS في Express. يمكنك تفعيل CORS لجميع المصادر باستخدام cors() أو تكوينه لمصادر محددة باستخدام cors(corsOptions).
مثال باستخدام Python مع Flask:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/resource")
def hello():
return {"message": "This is a CORS-enabled resource"}
if __name__ == '__main__':
app.run(debug=True)
توفر إضافة flask_cors طريقة بسيطة لتمكين CORS في تطبيقات Flask. يمكنك تمكين CORS لجميع المصادر عن طريق تمرير app إلى CORS(). كما يمكن أيضًا التكوين لمصادر محددة.
مثال باستخدام Java مع Spring Boot:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/resource")
.allowedOrigins("http://www.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("Content-Type", "X-Custom-Header")
.allowCredentials(true)
.maxAge(3600);
}
}
في Spring Boot، يمكنك تكوين CORS باستخدام WebMvcConfigurer. يسمح هذا بالتحكم الدقيق في المصادر والأساليب والترويسات المسموح بها وإعدادات CORS الأخرى.
إعداد ترويسات CORS مباشرة (مثال عام)
إذا كنت لا تستخدم أي إطار عمل، يمكنك تعيين الترويسات مباشرة في الكود من جانب الخادم (مثل PHP، Ruby on Rails، إلخ):
أفضل ممارسات CORS
لضمان تواصل آمن وفعال عبر المصادر، اتبع هذه الممارسات الأفضل:
- تجنب استخدام
Access-Control-Allow-Origin: *في بيئة الإنتاج: قد يشكل السماح لجميع المصادر بالوصول إلى مواردك خطرًا أمنيًا. بدلاً من ذلك، حدد المصادر المسموح بها بالضبط. - استخدم HTTPS: استخدم دائمًا HTTPS لكل من المصادر الطالبة والمخدمة لحماية البيانات أثناء النقل.
- تحقق من صحة الإدخال: تحقق دائمًا من صحة البيانات الواردة من الطلبات عبر المصادر وقم بتنقيتها لمنع هجمات الحقن.
- نفذ مصادقة وتفويضًا مناسبين: تأكد من أن المستخدمين المصرح لهم فقط يمكنهم الوصول إلى الموارد الحساسة.
- خزن الاستجابات التمهيدية مؤقتًا: استخدم
Access-Control-Max-Ageلتخزين الاستجابات التمهيدية مؤقتًا وتقليل عدد طلباتOPTIONS. - ضع في اعتبارك استخدام بيانات الاعتماد: إذا كانت واجهة برمجة التطبيقات الخاصة بك تتطلب مصادقة باستخدام ملفات تعريف الارتباط أو مصادقة HTTP، فأنت بحاجة إلى تعيين ترويسة
Access-Control-Allow-Credentialsإلىtrueعلى الخادم والخيارcredentialsإلى'include'في كود جافاسكريبت الخاص بك (على سبيل المثال، عند استخدامfetchأوXMLHttpRequest). كن حذرًا للغاية عند استخدام هذا الخيار، لأنه يمكن أن يؤدي إلى ثغرات أمنية إذا لم يتم التعامل معه بشكل صحيح. أيضًا، عند تعيين Access-Control-Allow-Credentials إلى true، لا يمكن تعيين Access-Control-Allow-Origin إلى "*". يجب عليك تحديد المصدر (المصادر) المسموح به بشكل صريح. - راجع تكوين CORS وحدثه بانتظام: مع تطور تطبيقك، راجع تكوين CORS وحدثه بانتظام للتأكد من أنه يظل آمنًا ويلبي احتياجاتك.
- افهم الآثار المترتبة على تكوينات CORS المختلفة: كن على دراية بالآثار الأمنية المترتبة على تكوينات CORS المختلفة واختر التكوين المناسب لتطبيقك.
- اختبر تنفيذ CORS الخاص بك: اختبر تنفيذ CORS الخاص بك بدقة للتأكد من أنه يعمل كما هو متوقع وأنه لا يقدم أي ثغرات أمنية. استخدم أدوات مطوري المتصفح لفحص طلبات الشبكة واستجاباتها، واستخدم أدوات الاختبار الآلي للتحقق من سلوك CORS.
مثال: استخدام Fetch API مع CORS
إليك مثال على كيفية استخدام واجهة برمجة التطبيقات fetch لتقديم طلب عبر المصادر:
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors', // يخبر المتصفح أن هذا طلب CORS
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
}
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
});
يخبر الخيار mode: 'cors' المتصفح أن هذا طلب CORS. إذا لم يسمح الخادم بالمصدر، فسيمنع المتصفح الوصول إلى الاستجابة، وسيتم طرح خطأ.
إذا كنت تستخدم بيانات الاعتماد (مثل ملفات تعريف الارتباط)، فأنت بحاجة إلى تعيين الخيار credentials إلى 'include':
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors',
credentials: 'include', // تضمين الكوكيز في الطلب
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
// ...
});
CORS و JSONP
JSON مع الحشو (JSONP) هو أسلوب أقدم لتجاوز سياسة نفس المصدر. يعمل عن طريق إنشاء علامة <script> ديناميكيًا تقوم بتحميل البيانات من نطاق مختلف. في حين أن JSONP يمكن أن يكون مفيدًا في مواقف معينة، إلا أنه يحتوي على قيود أمنية كبيرة ويجب تجنبه قدر الإمكان. CORS هو الحل المفضل للتواصل عبر المصادر لأنه يوفر آلية أكثر أمانًا ومرونة.
الاختلافات الرئيسية بين CORS و JSONP:
- الأمان: CORS أكثر أمانًا من JSONP لأنه يسمح للخادم بالتحكم في المصادر المسموح لها بالوصول إلى موارده. لا يوفر JSONP أي تحكم في المصدر.
- أساليب HTTP: يدعم CORS جميع أساليب HTTP (مثل
GET،POST،PUT،DELETE)، بينما يدعم JSONP طلباتGETفقط. - معالجة الأخطاء: يوفر CORS معالجة أخطاء أفضل من JSONP. عند فشل طلب CORS، يوفر المتصفح رسائل خطأ مفصلة. تقتصر معالجة أخطاء JSONP على اكتشاف ما إذا كان النص البرمجي قد تم تحميله بنجاح.
استكشاف أخطاء CORS وإصلاحها
قد يكون تصحيح أخطاء CORS محبطًا. إليك بعض النصائح الشائعة لاستكشاف الأخطاء وإصلاحها:
- تحقق من وحدة تحكم المتصفح: عادةً ما توفر وحدة تحكم المتصفح رسائل خطأ مفصلة حول مشكلات CORS.
- افحص طلبات الشبكة: استخدم أدوات مطوري المتصفح لفحص ترويسات HTTP لكل من الطلب والاستجابة. تحقق من تعيين ترويسات
OriginوAccess-Control-Allow-Originبشكل صحيح. - تحقق من تكوين جانب الخادم: تحقق مرة أخرى من تكوين CORS من جانب الخادم للتأكد من أنه يسمح بالمصادر والأساليب والترويسات الصحيحة.
- امسح ذاكرة التخزين المؤقت للمتصفح: في بعض الأحيان، يمكن أن تسبب الاستجابات التمهيدية المخزنة مؤقتًا مشكلات CORS. حاول مسح ذاكرة التخزين المؤقت للمتصفح أو استخدام نافذة تصفح خاصة.
- استخدم وكيل CORS: في بعض الحالات، قد تحتاج إلى استخدام وكيل CORS لتجاوز قيود CORS. ومع ذلك، كن على دراية بأن استخدام وكيل CORS يمكن أن يؤدي إلى مخاطر أمنية.
- ابحث عن التكوينات الخاطئة: ابحث عن التكوينات الخاطئة الشائعة مثل ترويسة
Access-Control-Allow-Originمفقودة، أو قيم غير صحيحة لـAccess-Control-Allow-MethodsأوAccess-Control-Allow-Headers، أو ترويسةOriginغير صحيحة في الطلب.
الخاتمة
تعد مشاركة الموارد عبر المصادر (CORS) آلية أساسية لتمكين التواصل الآمن عبر المصادر في تطبيقات جافاسكريبت. من خلال فهم سياسة نفس المصدر، وسير عمل CORS، وترويسات HTTP المختلفة المعنية، يمكن للمطورين تنفيذ CORS بفعالية لحماية تطبيقاتهم من الثغرات الأمنية مع السماح بالطلبات المشروعة عبر المصادر. يعد اتباع أفضل الممارسات لتكوين CORS ومراجعة التنفيذ بانتظام أمرًا بالغ الأهمية للحفاظ على تطبيق ويب آمن وقوي.
يقدم هذا الدليل الشامل أساسًا متينًا لفهم وتنفيذ CORS. تذكر الرجوع إلى الوثائق والموارد الرسمية لتقنيتك المحددة من جانب الخادم للتأكد من أنك تنفذ CORS بشكل صحيح وآمن.